#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <inttypes.h>
#include <sstream>
#include <string>
#include "assert_helper.h"
#include "charset_convert.h"
#include "file_helper.h"

namespace utility {

std::string case_adjust_for_path(const std::string & file)
{
	if (access(file.c_str(), F_OK) == 0) return file;	// 路径无需进行大小写调整
	DIR		*dir = NULL;
   struct	dirent *ent = NULL;
	std::string	directory;
	std::string stack(".");	// 路径堆栈默认以当前目录为栈底
	size_t	end = 0, tmp = 0;
	bool		flag = false;

	std::string path(file);
	if (file.length() > 3 /* for strlen("../")*/ &&
		0 == strncmp(file.c_str(), "../", strlen("../"))) {
		path = file.c_str() + 3 /*strlen("../")*/;
		stack = "..";
	} else if (file.length() > 2 /*strlen("./")*/ &&
		0 == strncmp(file.c_str(), "./", strlen("./"))) {
		path = file.c_str() + 2 /*strlen("./")*/;
		stack = ".";
	} else if (file.length() > 1 /*strlen("/")*/ && '/' == file[0]) {
		path = file.c_str() + 1 /*strlen("/")*/;
		stack = "/";
	}
	if (!path.empty()) {
		if (NULL == (dir = opendir(stack.c_str()))) {
		  return "";
		}
   	printf("We will find path %s\n", path.c_str());
		while (std::string::npos != end) {
			if ((std::string::npos != end)) {
				directory = path.substr(end + (0 == end ? 0 : 1),
				std::string::npos != (tmp = path.find_first_of('/', end + 1)) ? tmp - end - (0 == end ? 0 : 1): tmp);
				printf("Handle sub dir %s, pos %d - %d\n", directory.c_str(), end, tmp);
				if (directory.empty()) return stack + "/";	// 处理路径以 / 结尾的情况

				if (std::string::npos != tmp) {	// 不是最后一级路径
					while (NULL != (ent = readdir(dir))) {
						if ((DT_DIR == ent->d_type || DT_UNKNOWN == ent->d_type) &&
						0 == strcasecmp(ent->d_name, directory.c_str()) &&
						strcmp(ent->d_name, ".") &&
						strcmp(ent->d_name, ".."))
						{
							closedir(dir);
							if (1 == stack.length() && '/' == stack.at(0)) {
								stack += ent->d_name;
							} else {
								stack += std::string("/") + ent->d_name;
							}
							//printf("=>%s\n", stack.c_str());
							if (NULL == (dir = opendir(stack.c_str()))) {
								printf("Sub dir match failed.\n");
		  						return "";
							} else {
								flag = true;
								break;
							}
						}
					}
					if (!flag) {
						printf("Target path is not exist!\n");
						return "";
					}
				} else {	//最后一级路径
					while (NULL != (ent = readdir(dir))) {
						if ((DT_REG == ent->d_type || DT_DIR == ent->d_type ||
						DT_UNKNOWN == ent->d_type) &&
						0 == strcasecmp(ent->d_name, directory.c_str()) &&
						strcmp(ent->d_name, ".") &&
						strcmp(ent->d_name, ".."))
						{
							printf("Target path %s was found.\n", (stack + "/" + ent->d_name).c_str());
							closedir(dir);
							return stack + "/" + ent->d_name;
						}
					}
				}
			}
			end = tmp;
		}
	}
	printf("Target path is not exist!\n");
	return "";
}

bool	is_file_exist(const char * file)
{
	IF_NULL_RETURN_VALUE(file, false);
	if (access(file, F_OK) == 0) return true;
	return !case_adjust_for_path(file).empty();
}

bool
get_pe_file_version(const char *path, int & major, int & minor)
{
   IF_NULL_RETURN_VALUE(path, false);
   char   sample[] = "ERSION_INFO";
   TCHAR temp = 0;
   size_t  i = 0;
   char   flag = 0;
   FILE  *PE;

   PE = fopen(path, "rb");
   if (NULL != PE)
   {
      fread(&temp, sizeof(TCHAR), 1, PE);
      if (temp == 0X5A4D) /* 检查是否“PE”标志 */
      {
         while (!feof(PE))
         {
            fread(&temp, sizeof(TCHAR), 1, PE);

            if (temp == (TCHAR)'V')
            {
               flag = 1;
               for (i = 0; i < strlen(sample); ++i)
               {
                  fread(&temp, sizeof(TCHAR), 1, PE);
                  if ( temp != (int)(sample[i]) )
                  {
                     flag = 0;
                     break;
                  }
               }
            }

            if (0 != flag)
            {
               fseek(PE, 0X14L, SEEK_CUR);
               fread(&major, sizeof(int), 1, PE);
               fread(&minor, sizeof(int), 1, PE);
               fclose(PE);
               return true;
            }
         }
      }
      fclose(PE);
   }
   return false;
}

std::string
get_pe_file_version(const char *path)
{
   IF_NULL_RETURN_VALUE(path, false);
   int major = 0, minor = 0;
   if (get_pe_file_version(path, major, minor)) {
   	std::stringstream ver;
   	ver << static_cast<unsigned short>(major >> 16) << "." <<
   	static_cast<unsigned short>(major & 0XFFFF) << "." <<
   	static_cast<unsigned short>(minor >> 16) << "." <<
   	static_cast<unsigned short>(minor & 0XFFFF);
		return ver.str();
	}
	return "";
}

int	copy_file(const char * src, const char *dst)
{
	IF_NULL_RETURN_VALUE(src, -1);
	IF_NULL_RETURN_VALUE(dst, -1);
	const int BUFSIZE = 16384;
	char *buf = NULL;
	int sf = 0, df = 0, rb = 0, wb = 0;
	int going = 1;
	int e = 0;

	buf = new char [BUFSIZE];
	if (NULL != buf) {
		sf = open(src, O_RDONLY);
		if (sf < 0) {
			e = errno;
			printf("cpnt: %s: %s\n",src,strerror(e));
			delete [] buf;
			return(-1);
		}

		df = open(dst, O_WRONLY | O_CREAT, 00666);
		if (df < 0) {
			e = errno;
			printf("cpnt: %s: %s\n",dst,strerror(e));
			close(sf);
			delete [] buf;
			return(-1);
		}

		while (going) {
			rb = read(sf,buf,BUFSIZE);
			if (rb < 0) {
				e = errno;
				printf("cpnt: error while reading: %s\n",strerror(e));
				going = 0;
				break;
			}
			if (rb == 0) going = 0;
			wb = write(df,buf,rb);
			if (wb < 0) {
				e = errno;
				printf("cpnt: error while writing: %s\n",strerror(e));
				going = 0;
			}
		}

		close(sf);
		close(df);
		delete [] buf;
	}

	return(e ? -1 : 0);
}

}
